<template>
  <div class="audio-wrapper">
    <div class="box">
      <canvas id="oscilloscope" height="500" style="width: 100vw; height: 500px"></canvas>
    </div>
    <div class="oper-btns">
      <div ref="prevBtn" class="btn prev">上一曲</div>
      <div ref="playBtn" class="play pause"></div>
      <div ref="nextBtn" class="btn next">下一曲</div>
      <div class="btn oscilloscope">切换示波器</div>
    </div>
    <div
      ref="musicNameShowDom"
      id="js-music-name"
      style="text-align: center; margin-top: 20px; color: #ffffff"
    ></div>
    <audio
      ref="audioDom"
      id="audio1"
      style="display: none"
      controls
      crossOrigin="anonymous"
      src="./music1.mp3"
    ></audio>
  </div>
</template>

<script setup>
import { ref, reactive, onMounted } from "vue";

const audio = reactive({
  musics: [
    "./Flute.mp3",
    "./柏林之声.mp3",
    "./music1.mp3",
    "./风的季节.mp3",
    "./新白娘子传奇1.mp3",
    "./新白娘子传奇2.mp3",
  ],
  colors: ["	#FF0000", "#AEEEEE", "#B23AEE", "#FFFF00", "#EEDFCC"],
});

const analyser = ref(null);
const drawVisual = ref(null);
const canvas = ref(null);
const canvasCtx = ref(null);
const oscilloscopeBtn = ref(null);
const data = ref(new Uint8Array(512 / 2));
const volumeStep = ref(0.1);
const bufferLength = ref(0);
const isInit = ref(false);
const oscilloscopeFlag = ref(true);
const audioDom = ref();
const musicNameShowDom = ref();
const prevBtn = ref();
const playBtn = ref();
const nextBtn = ref();
const isPlay = ref(false);
// 处理requestAnimationFrame函数由于屏幕刷新率过高造成的动画执行太快的问题
const start = ref(0);

onMounted(() => {
  canvas.value = document.getElementById("oscilloscope");
  canvasCtx.value = canvas.value.getContext("2d");
  musicNameShowDom.value = document.querySelector("#js-music-name");
  oscilloscopeBtn.value = document.querySelector(".oscilloscope");
  const clientHeight = document.documentElement.clientWidth || document.body.clientHeight;
  canvas.value.setAttribute("width", clientHeight);
  canvasCtx.value.clearRect(0, 0, canvas.value.clientWidth, canvas.value.height);
  canvasCtx.value.fillStyle = "rgb(0,0,0)";
  canvasCtx.value.fillRect(0, 0, canvas.value.clientWidth, canvas.value.height);
  bindEvent();
});

const bindEvent = () => {
  musicNameShowDom.value.innerHTML = getCurrentPlayMusicLR();

  prevBtn.value.addEventListener(
    "click",
    () => {
      start.value = undefined;
      cancelAnimationFrame(drawVisual.value);
      audioDom.value.pause();
      const prevMusicName = getCurrentPlayMusicLR("prev");
      audioDom.value.setAttribute("src", prevMusicName);
      musicNameShowDom.value.innerHTML = prevMusicName;
      audioDom.value.play();
    },
    false
  );

  nextBtn.value.addEventListener(
    "click",
    () => {
      start.value = undefined;
      cancelAnimationFrame(drawVisual.value);
      audioDom.value.pause();
      const nextMusicName = getCurrentPlayMusicLR("next");
      audioDom.value.setAttribute("src", nextMusicName);
      musicNameShowDom.value.innerHTML = nextMusicName;
      audioDom.value.play();
    },
    false
  );

  document.addEventListener(
    "keydown",
    (ev) => {
      if (ev.code === "Space") {
        if (isPlay.value) {
          audioDom.value && audioDom.value.pause();
          isPlay.value = false;
        } else {
          audioDom.value && audioDom.value.play();
          isPlay.value = true;
        }
      } else if (ev.code === "ArrowLeft") {
        if (prevBtn.value) {
          prevBtn.value.click();
        }
      } else if (ev.code === "ArrowRight") {
        if (nextBtn.value) {
          nextBtn.value.click();
        }
      } else if (ev.code === "ArrowUp") {
        ev.preventDefault();
        if (audioDom.value) {
          let curVolume = audioDom.value.volume;
          if (curVolume < 1) {
            audioDom.value.volume += volumeStep.value;
            if (audioDom.value.volume > 1) {
              audioDom.value.volume = 1;
            }
          }
        }
      } else if (ev.code === "ArrowDown") {
        ev.preventDefault();
        if (audioDom.value) {
          let curVolume = audioDom.value.volume;
          if (curVolume > 0.1) {
            audioDom.value.volume -= volumeStep.value;
            if (audioDom.value.volume < 0) {
              audioDom.value.volume = 0;
            }
          }
        }
      }
    },
    false
  );

  playBtn.value.addEventListener(
    "click",
    () => {
      if (isPlay.value) {
        audioDom.value.pause();
        isPlay.value = false;
      } else {
        audioDom.value.play();
        isPlay.value = true;
      }
    },
    false
  );

  oscilloscopeBtn.value.addEventListener(
    "click",
    () => {
      start.value = undefined;
      oscilloscopeFlag.value = !oscilloscopeFlag.value;
      audioDom.value.pause();
      audioDom.value.play();
    },
    false
  );

  audioDom.value.addEventListener(
    "pause",
    () => {
      cancelAnimationFrame(drawVisual.value);
      playBtn.value && (playBtn.value.className = "play pause");
    },
    false
  );

  audioDom.value.addEventListener(
    "play",
    () => {
      playBtn.value.className = "play";
      if (isInit.value) {
        if (oscilloscopeFlag.value) {
          draw1();
        } else {
          draw2();
        }
        return;
      } else {
        let audioCtx = new AudioContext();
        let source = audioCtx.createMediaElementSource(audioDom.value);
        analyser.value = audioCtx.createAnalyser();
        analyser.value.fftSize = data.value.length;
        source.connect(analyser.value);
        analyser.value.connect(audioCtx.destination);
        analyser.value.getByteFrequencyData(data.value);
        // bufferLength = analyser.frequencyBinCount;
        bufferLength.value = data.value.length;
        if (oscilloscopeFlag.value) {
          draw1();
        } else {
          draw2();
        }
        isInit.value = true;
      }
    },
    false
  );

  audioDom.value.addEventListener(
    "ended",
    () => {
      nextBtn.value.click();
    },
    false
  );
};
// 获取 Media资源
const getCurrentPlayMusicLR = (tag) => {
  const curMusicName = audioDom.value.getAttribute("src");
  const musicsMaxIndex = audio.musics.length - 1;
  let curIndex = audio.musics.findIndex((item) => {
    return curMusicName == item;
  });
  if (tag === "prev") {
    if (curIndex === 0) {
      return audio.musics[musicsMaxIndex];
    } else {
      return audio.musics[curIndex - 1];
    }
  } else if (tag === "next") {
    if (curIndex === musicsMaxIndex) {
      return audio.musics[0];
    } else {
      return audio.musics[curIndex + 1];
    }
  } else {
    return curMusicName;
  }
};

// 曲线 示波器
const draw1 = () => {
  drawVisual.value = requestAnimationFrame(draw1);
  analyser.value.getByteTimeDomainData(data.value);
  canvasCtx.value.fillStyle = "rgb(0,0,0)";
  canvasCtx.value.fillRect(0, 0, canvas.value.clientWidth, canvas.value.height);
  canvasCtx.value.lineWidth = 3;
  // 波浪条颜色
  canvasCtx.value.strokeStyle =
    audio.colors[Math.round(Math.random() * audio.colors.length)];
  canvasCtx.value.beginPath();
  var sliceWidth = (canvas.value.width * 1.0) / bufferLength.value;
  var x = 0;
  for (var i = 0; i < bufferLength.value; i++) {
    var v = data.value[i] / 256;
    var y = v * canvas.value.height;
    if (i === 0) {
      canvasCtx.value.moveTo(x, y);
    } else {
      canvasCtx.value.lineTo(x, y);
    }
    x += sliceWidth;
  }
  canvasCtx.value.lineTo(canvas.value.width, canvas.value.height / 2);
  canvasCtx.value.stroke();
};

// 上下 示波器
const draw2 = (timestamp) => {
  drawVisual.value = requestAnimationFrame(draw2);
  // start只会被赋值一次？ 第一次是undefined 但是往后就不会再试undefined了
  if (!start.value) {
    start.value = timestamp;
  }
  let elapsed = timestamp - start.value;
  if (elapsed > 100) {
    start.value = undefined;
    canvasCtx.value.clearRect(0, 0, canvas.value.clientWidth, canvas.value.height);
    analyser.value.getByteTimeDomainData(data.value);
    canvasCtx.value.fillStyle = "rgb(0,0,0)";
    canvasCtx.value.fillRect(0, 0, canvas.value.clientWidth, canvas.value.height);
    // 柱状颜色1
    canvasCtx.value.fillStyle = "#FFFF00";
    // 柱状颜色2
    // canvasCtx.value.fillStyle = audio.colors[Math.round(Math.random() * audio.colors.length)];
    let barWidth = canvas.value.clientWidth / bufferLength.value;
    for (let i = 0; i < bufferLength.value; i++) {
      // 当前值除以最大值 获取到得值 不会大于1 乘以画布高度 拿到最终高度
      let barHeight = (data.value[i] / 256) * canvas.value.height;
      let x = barWidth * i;
      // 底部开始
      let y = canvas.value.height - barHeight;
      // let y = canvas.height - barHeight + (canvas.height - barHeight);
      canvasCtx.value.fillRect(x, y, barWidth - 3, barHeight);
    }
  }
};
</script>

<style scoped>
.audio-wrapper {
  width: 100%;
  height: 100%;
  background-color: #000000;
}
.oper-btns {
  margin: 0 auto;
  display: flex;
  justify-content: center;
  align-content: center;
  align-items: center;
  background-color: #ffffff;
  border-radius: 5px;
  overflow: hidden;
}

.oper-btns div {
  padding: 10px 12px;
  border-radius: 5px;
  user-select: none;
  overflow: hidden;
}

.oper-btns .play {
  width: 10px;
  height: 10px;
  background-image: url(../assets/audio/pause.png);
  background-repeat: no-repeat;
  background-size: 100% 100%;
  cursor: pointer;
}

.oper-btns .play.pause {
  width: 10px;
  height: 10px;
  background-image: url(../assets/audio/play.png) !important;
  background-repeat: no-repeat;
  background-size: 100% 100%;
  cursor: pointer;
}

.oper-btns .btn:hover {
  background-color: #ffffff;
  color: #000000;
  cursor: pointer;
  mix-blend-mode: difference;
}
</style>
